/*
 * Created on 14-Feb-2005
 * Created by Paul Gardner
 * Copyright (C) Azureus Software, Inc, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

package org.gudy.azureus2.core3.tracker.client.impl.dht;

import java.net.URL;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.gudy.azureus2.core3.internat.MessageText;
import org.gudy.azureus2.core3.logging.LogEvent;
import org.gudy.azureus2.core3.logging.LogIDs;
import org.gudy.azureus2.core3.logging.Logger;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.torrent.TOTorrentAnnounceURLSet;
import org.gudy.azureus2.core3.torrent.TOTorrentException;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncer;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerDataProvider;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerException;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerListener;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponse;
import org.gudy.azureus2.core3.tracker.client.TRTrackerAnnouncerResponsePeer;
import org.gudy.azureus2.core3.tracker.client.impl.TRTrackerAnnouncerHelper;
import org.gudy.azureus2.core3.tracker.client.impl.TRTrackerAnnouncerImpl;
import org.gudy.azureus2.core3.tracker.client.impl.TRTrackerAnnouncerResponseImpl;
import org.gudy.azureus2.core3.tracker.client.impl.TRTrackerAnnouncerResponsePeerImpl;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.HashWrapper;
import org.gudy.azureus2.core3.util.IndentWriter;
import org.gudy.azureus2.core3.util.SystemTime;
import org.gudy.azureus2.core3.util.TorrentUtils;
import org.gudy.azureus2.plugins.clientid.ClientIDException;
import org.gudy.azureus2.plugins.download.DownloadAnnounceResult;
import org.gudy.azureus2.plugins.download.DownloadAnnounceResultPeer;
import org.gudy.azureus2.pluginsimpl.local.clientid.ClientIDManagerImpl;

import com.aelitis.azureus.core.tracker.TrackerPeerSource;

/**
 * @author parg
 * 
 */

public class TRTrackerDHTAnnouncerImpl implements TRTrackerAnnouncerHelper {
    public final static LogIDs LOGID = LogIDs.TRACKER;

    private TOTorrent torrent;
    private HashWrapper torrent_hash;

    private TRTrackerAnnouncerImpl.Helper helper;

    private byte[] data_peer_id;

    private String tracker_status_str;
    private long last_update_time;

    private int state = TS_INITIALISED;

    private TRTrackerAnnouncerResponseImpl last_response;

    private boolean manual;

    public TRTrackerDHTAnnouncerImpl(TOTorrent _torrent, String[] _networks, boolean _manual, TRTrackerAnnouncerImpl.Helper _helper)

    throws TRTrackerAnnouncerException {
        torrent = _torrent;
        manual = _manual;
        helper = _helper;

        try {
            torrent_hash = torrent.getHashWrapper();

        } catch (TOTorrentException e) {

            Debug.printStackTrace(e);
        }
        try {
            data_peer_id = ClientIDManagerImpl.getSingleton().generatePeerID(torrent, false);

        } catch (ClientIDException e) {

            throw (new TRTrackerAnnouncerException("TRTrackerAnnouncer: Peer ID generation fails", e));
        }

        last_response =
                new TRTrackerAnnouncerResponseImpl(torrent.getAnnounceURL(), torrent_hash, TRTrackerAnnouncerResponse.ST_OFFLINE, 0, "Initialising");

        tracker_status_str = MessageText.getString("PeerManager.status.checking") + "...";
    }

    public void setAnnounceDataProvider(TRTrackerAnnouncerDataProvider provider) {
    }

    public boolean isManual() {
        return (manual);
    }

    public TOTorrent getTorrent() {
        return (torrent);
    }

    public URL getTrackerURL() {
        return (TorrentUtils.getDecentralisedURL(torrent));
    }

    public void setTrackerURL(URL url) {
        Debug.out("Not implemented");
    }

    public TOTorrentAnnounceURLSet[] getAnnounceSets() {
        return (new TOTorrentAnnounceURLSet[] { torrent.getAnnounceURLGroup().createAnnounceURLSet(
                new URL[] { TorrentUtils.getDecentralisedURL(torrent) }) });
    }

    public void resetTrackerUrl(boolean shuffle) {
    }

    public void setIPOverride(String override) {
    }

    public void clearIPOverride() {
    }

    public int getPort() {
        return (0);
    }

    public byte[] getPeerId() {
        return (data_peer_id);
    }

    public void setRefreshDelayOverrides(int percentage) {
    }

    public int getTimeUntilNextUpdate() {
        long elapsed = (SystemTime.getCurrentTime() - last_update_time) / 1000;

        return ((int) (last_response.getTimeToWait() - elapsed));
    }

    public int getLastUpdateTime() {
        return ((int) (last_update_time / 1000));
    }

    public void update(boolean force) {
        state = TS_DOWNLOADING;
    }

    public void complete(boolean already_reported) {
        state = TS_COMPLETED;
    }

    public void stop(boolean for_queue) {
        state = TS_STOPPED;
    }

    public void destroy() {
    }

    public int getStatus() {
        return (state);
    }

    public String getStatusString() {
        return (tracker_status_str);
    }

    public TRTrackerAnnouncer getBestAnnouncer() {
        return (this);
    }

    public TRTrackerAnnouncerResponse getLastResponse() {
        return (last_response);
    }

    public boolean isUpdating() {
        return (false);
    }

    public long getInterval() {
        return (-1);
    }

    public long getMinInterval() {
        return (-1);
    }

    public void refreshListeners() {
    }

    public void setAnnounceResult(DownloadAnnounceResult result) {
        last_update_time = SystemTime.getCurrentTime();

        TRTrackerAnnouncerResponseImpl response;

        if (result.getResponseType() == DownloadAnnounceResult.RT_ERROR) {

            tracker_status_str = MessageText.getString("PeerManager.status.error");

            String reason = result.getError();

            if (reason != null) {

                tracker_status_str += " (" + reason + ")";
            }

            response =
                    new TRTrackerAnnouncerResponseImpl(result.getURL(), torrent_hash, TRTrackerAnnouncerResponse.ST_OFFLINE,
                            result.getTimeToWait(), reason);
        } else {
            DownloadAnnounceResultPeer[] ext_peers = result.getPeers();

            TRTrackerAnnouncerResponsePeerImpl[] peers = new TRTrackerAnnouncerResponsePeerImpl[ext_peers.length];

            for (int i = 0; i < ext_peers.length; i++) {

                DownloadAnnounceResultPeer ext_peer = ext_peers[i];

                if (Logger.isEnabled())
                    Logger.log(new LogEvent(torrent, LOGID, "EXTERNAL PEER DHT: ip=" + ext_peer.getAddress() + ",port=" + ext_peer.getPort()
                            + ",prot=" + ext_peer.getProtocol()));

                int http_port = 0;
                byte az_version = TRTrackerAnnouncer.AZ_TRACKER_VERSION_1;

                peers[i] =
                        new TRTrackerAnnouncerResponsePeerImpl(ext_peer.getSource(), ext_peer.getPeerID(), ext_peer.getAddress(), ext_peer
                                .getPort(), ext_peer.getUDPPort(), http_port, ext_peer.getProtocol(), az_version, (short) 0);
            }

            helper.addToTrackerCache(peers);

            tracker_status_str = MessageText.getString("PeerManager.status.ok");

            response =
                    new TRTrackerAnnouncerResponseImpl(result.getURL(), torrent_hash, TRTrackerAnnouncerResponse.ST_ONLINE, result.getTimeToWait(),
                            peers);
        }

        last_response = response;

        TRTrackerAnnouncerResponsePeer[] peers = response.getPeers();

        if (peers == null || peers.length < 5) {

            TRTrackerAnnouncerResponsePeer[] cached_peers = helper.getPeersFromCache(100);

            if (cached_peers.length > 0) {

                Set<TRTrackerAnnouncerResponsePeer> new_peers =
                        new TreeSet<TRTrackerAnnouncerResponsePeer>(new Comparator<TRTrackerAnnouncerResponsePeer>() {
                            public int compare(TRTrackerAnnouncerResponsePeer o1, TRTrackerAnnouncerResponsePeer o2) {
                                return (o1.compareTo(o2));
                            }
                        });

                if (peers != null) {

                    new_peers.addAll(Arrays.asList(peers));
                }

                new_peers.addAll(Arrays.asList(cached_peers));

                response.setPeers(new_peers.toArray(new TRTrackerAnnouncerResponsePeer[new_peers.size()]));
            }
        }

        helper.informResponse(this, response);
    }

    public void addListener(TRTrackerAnnouncerListener l) {
        helper.addListener(l);
    }

    public void removeListener(TRTrackerAnnouncerListener l) {
        helper.removeListener(l);
    }

    public void setTrackerResponseCache(Map map) {
        helper.setTrackerResponseCache(map);
    }

    public void removeFromTrackerResponseCache(String ip, int tcpPort) {
        helper.removeFromTrackerResponseCache(ip, tcpPort);
    }

    public Map getTrackerResponseCache() {
        return (helper.getTrackerResponseCache());
    }

    public TrackerPeerSource getTrackerPeerSource(TOTorrentAnnounceURLSet set) {
        Debug.out("not implemented");

        return null;
    }

    public TrackerPeerSource getCacheTrackerPeerSource() {
        Debug.out("not implemented");

        return null;
    }

    public void generateEvidence(IndentWriter writer) {
        writer.println("DHT announce: " + (last_response == null ? "null" : last_response.getString()));
    }
}
